home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / inn1.000 / inn1.4sec-linux-src.tar / inn / frontends / rnews.c < prev    next >
C/C++ Source or Header  |  1993-03-18  |  20KB  |  909 lines

  1. /*  $Revision: 1.24 $
  2. **
  3. **  A front-end for InterNetNews.
  4. **  Read UUCP batches and offer them up NNTP-style.  Because we may end
  5. **  up sending our input down a pipe to uncompress, we have to be careful
  6. **  to do unbuffered reads.
  7. */
  8. #include "configdata.h"
  9. #include <stdio.h>
  10. #include <ctype.h>
  11. #include <sys/types.h>
  12. #if    defined(DO_NEED_TIME)
  13. #include <time.h>
  14. #endif    /* defined(DO_NEED_TIME) */
  15. #include <sys/time.h>
  16. #include <fcntl.h>
  17. #include <sys/stat.h>
  18. #include <errno.h>
  19. #include "nntp.h"
  20. #include "paths.h"
  21. #include "logging.h"
  22. #include "libinn.h"
  23. #include "clibrary.h"
  24. #include "mydir.h"
  25. #include "macros.h"
  26.  
  27.  
  28. typedef struct _HEADER {
  29.     STRING    Name;
  30.     int        size;
  31. } HEADER;
  32.  
  33.  
  34. STATIC BOOL    Verbose;
  35. STATIC char    *InputFile = "stdin";
  36. STATIC char    *UUCPHost;
  37. STATIC char    SPOOLNEWS[] = _PATH_SPOOLNEWS;
  38. STATIC char    SPOOLTEMP[] = _PATH_SPOOLTEMP;
  39. STATIC FILE    *FromServer;
  40. STATIC FILE    *ToServer;
  41. STATIC char    UNPACK[] = "news-unpack";
  42. STATIC HEADER    RequiredHeaders[] = {
  43.     { "Message-ID",    10 },
  44. #define _messageid    0
  45.     { "Newsgroups",    10 },
  46. #define _newsgroups    1
  47.     { "From",         4 },
  48. #define _from        2
  49.     { "Date",         4 },
  50. #define _date        3
  51.     { "Subject",     7 },
  52. #define _subject    4
  53.     { "Path",         4 },
  54. #define _path        5
  55. };
  56. #define IS_MESGID(hp)    ((hp) == &RequiredHeaders[_messageid])
  57. #define IS_PATH(hp)    ((hp) == &RequiredHeaders[_path])
  58.  
  59.  
  60.  
  61. /*
  62. **  Do perror, making sure errno is preserved.
  63. */
  64. STATIC void
  65. xperror(p)
  66.     char    *p;
  67. {
  68.     int        oerrno;
  69.  
  70.     oerrno = errno;
  71.     perror(p);
  72.     errno = oerrno;
  73. }
  74.  
  75.  
  76. /*
  77. **  Open up a pipe to a process with fd tied to its stdin.  Return a
  78. **  descriptor tied to its stdout or -1 on error.
  79. */
  80. STATIC int
  81. StartChild(fd, path, argv)
  82.     int        fd;
  83.     char    *path;
  84.     char    *argv[];
  85. {
  86.     int        pan[2];
  87.     int        i;
  88.     int        pid;
  89.  
  90.     /* Create a pipe. */
  91.     if (pipe(pan) < 0) {
  92.     syslog(L_FATAL, "cant pipe for %s %m", path);
  93.     exit(1);
  94.     }
  95.  
  96.     /* Get a child. */
  97.     for (i = 0; (pid = FORK()) < 0; i++) {
  98.     if (i == MAX_FORKS) {
  99.         syslog(L_ERROR, "cant fork %s %m -- spooling", path);
  100.         return -1;
  101.     }
  102.     syslog(L_NOTICE, "cant fork %s -- waiting", path);
  103.     (void)sleep(60);
  104.     }
  105.  
  106.     /* Run the child, with redirection. */
  107.     if (pid == 0) {
  108.     (void)close(pan[PIPE_READ]);
  109.  
  110.     /* Stdin comes from our old input. */
  111.     if (fd != STDIN) {
  112.         if ((i = dup2(fd, STDIN)) != STDIN) {
  113.         syslog(L_FATAL, "cant dup2 %d to 0 got %d %m", fd, i);
  114.         _exit(1);
  115.         }
  116.         (void)close(fd);
  117.     }
  118.  
  119.     /* Stdout goes down the pipe. */
  120.     if (pan[PIPE_WRITE] != STDOUT) {
  121.         if ((i = dup2(pan[PIPE_WRITE], STDOUT)) != STDOUT) {
  122.         syslog(L_FATAL, "cant dup2 %d to 1 got %d %m",
  123.             pan[PIPE_WRITE], i);
  124.         _exit(1);
  125.         }
  126.         (void)close(pan[PIPE_WRITE]);
  127.     }
  128.  
  129.     (void)execv(path, argv);
  130.     syslog(L_FATAL, "cant execv %s %m", path);
  131.     _exit(1);
  132.     }
  133.  
  134.     (void)close(pan[PIPE_WRITE]);
  135.     (void)close(fd);
  136.     return pan[PIPE_READ];
  137. }
  138.  
  139.  
  140. /*
  141. **  Wait for the specified number of children.
  142. */
  143. STATIC void
  144. WaitForChildren(i)
  145.     register int    i;
  146. {
  147.     register int    pid;
  148.     int            status;
  149.  
  150.     while (--i >= 0) {
  151.     pid = waitnb(&status);
  152.     if (pid < 0) {
  153.         if (errno != ECHILD)
  154.         syslog(L_ERROR, "cant wait %m");
  155.         break;
  156.     }
  157.     }
  158. }
  159.  
  160.  
  161.  
  162.  
  163. /*
  164. **  Make a temporary filename that is unlikely to collide with mktemp
  165. **  or cause problems for sites with 14-character filename limits,
  166. **  and that hints at the sending host.
  167. */
  168. STATIC void
  169. TempName(dir, buff)
  170.     char    *dir;
  171.     char    *buff;
  172. {
  173.     (void)sprintf(buff, "%s/%.8sXXXXXX", dir, UUCPHost ? UUCPHost : "unknown");
  174.     (void)mktemp(buff);
  175. }
  176.  
  177.  
  178. /*
  179. **  Clean up the NNTP escapes from a line.
  180. */
  181. STATIC char *
  182. REMclean(buff)
  183.     char    *buff;
  184. {
  185.     char    *p;
  186.  
  187.     if ((p = strchr(buff, '\r')) != NULL)
  188.     *p = '\0';
  189.     if ((p = strchr(buff, '\n')) != NULL)
  190.     *p = '\0';
  191.  
  192.     /* The dot-escape is only in text, not command responses. */
  193.     return buff;
  194. }
  195.  
  196.  
  197. /*
  198. **  Write an article to the rejected directory.
  199. */
  200. STATIC void
  201. Reject(article, reason, arg)
  202.     char    *article;
  203.     char    *reason;
  204.     char    *arg;
  205. {
  206.     char    buff[SMBUF];
  207.     FILE    *F;
  208.     int        i;
  209.  
  210.     syslog(L_NOTICE, reason, arg);
  211.     if (Verbose) {
  212.     (void)fprintf(stderr, "%s: ", InputFile);
  213.     (void)fprintf(stderr, reason, arg);
  214.     (void)fprintf(stderr, " [%.40s...]\n", article);
  215.     }
  216.  
  217.     TempName(_PATH_BADNEWS, buff);
  218.     if ((F = fopen(buff, "w")) == NULL) {
  219.     syslog(L_ERROR, "cant fopen %s %m", buff);
  220.     return;
  221.     }
  222.     i = strlen(article);
  223.     if (fwrite((POINTER)article, (SIZE_T)1, (SIZE_T)i, F) != i)
  224.     syslog(L_ERROR, "cant fwrite %s %m", buff);
  225.     if (fclose(F) == EOF)
  226.     syslog(L_ERROR, "cant close %s %m", buff);
  227. }
  228.  
  229.  
  230. /*
  231. **  Process one article.  Return TRUE if the article was okay; FALSE if the
  232. **  whole batch needs to be saved (such as when the server goes down or if
  233. **  the file is corrupted).
  234. */
  235. STATIC BOOL
  236. Process(article)
  237.     char        *article;
  238. {
  239.     register HEADER    *hp;
  240.     register char    *p;
  241.     char        *id;
  242.     char        buff[SMBUF];
  243. #if    defined(FILE_RNEWS_LOG_DUPS)
  244.     FILE        *F;
  245. #endif    /* defined(FILE_RNEWS_LOG_DUPS) */
  246. #if    !defined(DONT_RNEWS_LOG_DUPS)
  247.     char        path[40];
  248. #endif    /* !defined(DONT_RNEWS_LOG_DUPS) */
  249.  
  250.     /* Empty article? */
  251.     if (*article == '\0')
  252.     return TRUE;
  253.  
  254.     /* Make sure that all the headers are there, note the ID. */
  255.     for (hp = RequiredHeaders; hp < ENDOF(RequiredHeaders); hp++) {
  256.     if ((p = HeaderFind(article, hp->Name, hp->size)) == NULL) {
  257.         Reject(article, "bad_article missing %s", hp->Name);
  258.         return TRUE;
  259.     }
  260.     if (IS_MESGID(hp)) {
  261.         id = p;
  262.         continue;
  263.     }
  264. #if    !defined(DONT_RNEWS_LOG_DUPS)
  265.     if (IS_PATH(hp)) {
  266.         (void)strncpy(path, p, sizeof path);
  267.         path[sizeof path - 1] = '\0';
  268.         if ((p = strchr(path, '\n')) != NULL)
  269.         *p = '\0';
  270.     }
  271. #endif    /* !defined(DONT_RNEWS_LOG_DUPS) */
  272.     }
  273.  
  274.     /* Send the NNTP "ihave" message. */
  275.     if ((p = strchr(id, '\n')) == NULL) {
  276.     Reject(article, "bad_article unterminated %s header", "Message-ID");
  277.     return TRUE;
  278.     }
  279.     *p = '\0';
  280.     (void)fprintf(ToServer, "ihave %s\r\n", id);
  281.     (void)fflush(ToServer);
  282.     if (UUCPHost)
  283.     syslog(L_NOTICE, "offered %s %s", id, UUCPHost);
  284.     *p = '\n';
  285.  
  286.     /* Get a reply, see if they want the article. */
  287.     if (fgets(buff, sizeof buff, FromServer) == NULL) {
  288.     syslog(L_ERROR, "cant fgets after ihave %m");
  289.     return FALSE;
  290.     }
  291.     (void)REMclean(buff);
  292.     if (!CTYPE(isdigit, buff[0])) {
  293.     syslog(L_NOTICE, "bad_reply after ihave %s", buff);
  294.     return FALSE;
  295.     }
  296.     switch (atoi(buff)) {
  297.     default:
  298.     Reject(article, "unknown_reply after ihave %s", buff);
  299.     return TRUE;
  300.     case NNTP_SENDIT_VAL:
  301.     break;
  302.     case NNTP_HAVEIT_VAL:
  303. #if    defined(SYSLOG_RNEWS_LOG_DUPS)
  304.     *p = '\0';
  305.     syslog(L_NOTICE, "duplicate %s %s", id, path);
  306. #endif    /* defined(SYSLOG_RNEWS_LOG_DUPS) */
  307. #if    defined(FILE_RNEWS_LOG_DUPS)
  308.     if ((F = fopen(_PATH_RNEWS_DUP_LOG, "a")) != NULL) {
  309.         *p = '\0';
  310.         (void)fprintf(stderr, "duplicate %s %s", id, path);
  311.         (void)fclose(F);
  312.     }
  313. #endif    /* defined(FILE_RNEWS_LOG_DUPS) */
  314.     return TRUE;
  315.     }
  316.  
  317.     /* Send all the lines in the article, escaping periods. */
  318.     if (NNTPsendarticle(article, ToServer, TRUE) < 0) {
  319.     syslog(L_NOTICE, "cant sendarticle %m");
  320.     return FALSE;
  321.     }
  322.  
  323.     /* Process server reply code. */
  324.     if (fgets(buff, sizeof buff, FromServer) == NULL) {
  325.     syslog(L_ERROR, "cant fgets after article %m");
  326.     return FALSE;
  327.     }
  328.     if ((p = strchr(buff, '\r')) != NULL || (p = strchr(buff, '\n')) != NULL)
  329.     *p = '\0';
  330.     if (!CTYPE(isdigit, buff[0])) {
  331.     syslog(L_NOTICE, "bad_reply after article %s", buff);
  332.     return FALSE;
  333.     }
  334.     switch (atoi(buff)) {
  335.     default:
  336.     syslog(L_NOTICE, "unknown_reply after article %s", buff);
  337.     /* FALLTHROUGH */
  338.     case NNTP_RESENDIT_VAL:
  339.     return FALSE;
  340.     case NNTP_TOOKIT_VAL:
  341.     break;
  342.     case NNTP_REJECTIT_VAL:
  343. #if    defined(DO_RNEWS_SAVE_BAD)
  344.     Reject(article, "rejected %s", buff);
  345. #else
  346.     syslog(L_NOTICE, "rejected %s", buff);
  347. #endif    /* defined(DO_RNEWS_SAVE_BAD) */
  348.     break;
  349.     }
  350.     return TRUE;
  351. }
  352.  
  353.  
  354. /*
  355. **  Read the rest of the input as an article.  Just punt to stdio in
  356. **  this case and let it do the buffering.
  357. */
  358. STATIC BOOL
  359. ReadRemainder(fd, first, second)
  360.     register int    fd;
  361.     char        first;
  362.     char        second;
  363. {
  364.     register FILE    *F;
  365.     register char    *article;
  366.     register int    size;
  367.     register int    used;
  368.     register int    left;
  369.     register int    i;
  370.     BOOL        ok;
  371.  
  372.     /* Turn the descriptor into a stream. */
  373.     if ((F = fdopen(fd, "r")) == NULL) {
  374.     syslog(L_FATAL, "can't fdopen %d %m", fd);
  375.     exit(1);
  376.     }
  377.  
  378.     /* Get an initial allocation, leaving space for the \0. */
  379.     size = BUFSIZ + 1;
  380.     article = NEW(char, size + 2);
  381.     article[0] = first;
  382.     article[1] = second;
  383.     used = second ? 2 : 1;
  384.     left = size - used;
  385.  
  386.     /* Read the input. */
  387.     while ((i = fread((POINTER)&article[used], (SIZE_T)1,
  388.             (SIZE_T)left, F)) != 0) {
  389.     if (i < 0) {
  390.         syslog(L_FATAL, "cant fread after %d bytes %m", used);
  391.         exit(1);
  392.     }
  393.     used += i;
  394.     left -= i;
  395.     if (left < SMBUF) {
  396.         size += BUFSIZ;
  397.         left += BUFSIZ;
  398.         RENEW(article, char, size);
  399.     }
  400.     }
  401.     if (article[used - 1] != '\n')
  402.     article[used++] = '\n';
  403.     article[used] = '\0';
  404.     (void)fclose(F);
  405.  
  406.     ok = Process(article);
  407.     DISPOSE(article);
  408.     return ok;
  409. }
  410.  
  411.  
  412. /*
  413. **  Read an article from the input stream that is artsize bytes long.
  414. */
  415. STATIC BOOL
  416. ReadBytecount(fd, artsize)
  417.     register int    fd;
  418.     int            artsize;
  419. {
  420.     static char        *article;
  421.     static int        oldsize;
  422.     register char    *p;
  423.     register int    left;
  424.     register int    i;
  425.  
  426.     /* If we haven't gotten any memory before, or we didn't get enough,
  427.      * then get some. */
  428.     if (article == NULL) {
  429.     oldsize = artsize;
  430.     article = NEW(char, oldsize + 1 + 1);
  431.     }
  432.     else if (artsize > oldsize) {
  433.     oldsize = artsize;
  434.     RENEW(article, char, oldsize + 1 + 1);
  435.     }
  436.  
  437.     /* Read in the article. */
  438.     for (p = article, left = artsize; left; p += i, left -= i)
  439.     if ((i = read(fd, p, left)) <= 0) {
  440.         i = errno;
  441.         syslog(L_ERROR, "cant read wanted %d got %d %m",
  442.         artsize, artsize - left);
  443. #if    0
  444.         /* Don't do this -- if the article gets re-processed we
  445.          * will end up accepting the truncated version. */
  446.         artsize = p - article;
  447.         article[artsize] = '\0';
  448.         Reject(article, "short read (%s?)", strerror(i));
  449. #endif    /* 0 */
  450.         return TRUE;
  451.     }
  452.     if (p[-1] != '\n')
  453.     *p++ = '\n';
  454.     *p = '\0';
  455.  
  456.     return Process(article);
  457. }
  458.  
  459.  
  460.  
  461. /*
  462. **  Read a single text line; not unlike fgets().  Just more inefficient.
  463. */
  464. STATIC BOOL
  465. ReadLine(p, size, fd)
  466.     char    *p;
  467.     int        size;
  468.     int        fd;
  469. {
  470.     char    *save;
  471.  
  472.     /* Fill the buffer, a byte at a time. */
  473.     for (save = p; size > 0; p++, size--) {
  474.     if (read(fd, p, 1) != 1) {
  475.         *p = '\0';
  476.         syslog(L_FATAL, "cant read first line got %s %m", save);
  477.         exit(1);
  478.     }
  479.     if (*p == '\n') {
  480.         *p = '\0';
  481.         return TRUE;
  482.     }
  483.     }
  484.     *p = '\0';
  485.     syslog(L_FATAL, "bad_line too long %s", save);
  486.     return FALSE;
  487. }
  488.  
  489.  
  490. /*
  491. **  Unpack a single batch.
  492. */
  493. STATIC BOOL
  494. UnpackOne(fdp, countp)
  495.     int        *fdp;
  496.     int        *countp;
  497. {
  498. #if    defined(DO_RNEWSPROGS)
  499.     char    path[sizeof _PATH_RNEWSPROGS + 1 + SMBUF + 1];
  500.     char    *p;
  501. #endif    /* defined(DO_RNEWSPROGS) */
  502.     char    buff[SMBUF];
  503.     STRING    cargv[4];
  504.     int        artsize;
  505.     int        i;
  506.     BOOL    HadCount;
  507.     BOOL    SawCunbatch;
  508.  
  509.     *countp = 0;
  510.     for (SawCunbatch = FALSE, HadCount = FALSE; ; ) {
  511.     /* Get the first character. */
  512.     if ((i = read(*fdp, &buff[0], 1)) < 0) {
  513.         syslog(L_ERROR, "cant read first character %m");
  514.         return FALSE;
  515.     }
  516.     if (i == 0)
  517.         break;
  518.  
  519.     if (buff[0] != RNEWS_MAGIC1)
  520.         /* Not a batch file.  If we already got one count, the batch
  521.          * is corrupted, else read rest of input as an article. */
  522.         return HadCount ? FALSE : ReadRemainder(*fdp, buff[0], '\0');
  523.  
  524.     /* Get the second character. */
  525.     if ((i = read(*fdp, &buff[1], 1)) < 0) {
  526.         syslog(L_ERROR, "cant read second character %m");
  527.         return FALSE;
  528.     }
  529.     if (i == 0)
  530.         /* A one-byte batch? */
  531.         return FALSE;
  532.  
  533.     /* Check second magic character. */
  534.     if (buff[1] != RNEWS_MAGIC2)
  535.         return HadCount ? FALSE : ReadRemainder(*fdp, buff[0], buff[1]);
  536.  
  537.     /* Some kind of batch -- get the command. */
  538.     if (!ReadLine(&buff[2], (int)(sizeof buff - 3), *fdp))
  539.         return FALSE;
  540.  
  541.     if (strncmp(buff, "#! rnews ", 9) == 0) {
  542.         artsize = atoi(&buff[9]);
  543.         if (artsize <= 0) {
  544.         syslog(L_ERROR, "bad_line bad count %s", buff);
  545.         return FALSE;
  546.         }
  547.         HadCount = TRUE;
  548.         if (ReadBytecount(*fdp, artsize))
  549.         continue;
  550.         return FALSE;
  551.     }
  552.  
  553.     if (HadCount)
  554.         /* Already saw a bytecount -- probably corrupted. */
  555.         return FALSE;
  556.  
  557.     if (strcmp(buff, "#! cunbatch") == 0) {
  558.         if (SawCunbatch) {
  559.         syslog(L_ERROR, "nested_cunbatch");
  560.         return FALSE;
  561.         }
  562.         cargv[0] = UNPACK;
  563.         cargv[1] = "-d";
  564.         cargv[2] = NULL;
  565.         *fdp = StartChild(*fdp, _PATH_COMPRESS, cargv);
  566.         if (*fdp < 0)
  567.         return FALSE;
  568.         (*countp)++;
  569.         SawCunbatch = TRUE;
  570.         continue;
  571.     }
  572.  
  573. #if    defined(DO_RNEWSPROGS)
  574.     cargv[0] = UNPACK;
  575.     cargv[1] = NULL;
  576.     /* Ignore any possible leading pathnames, to avoid trouble. */
  577.     if ((p = strrchr(&buff[3], '/')) != NULL)
  578.         p++;
  579.     else
  580.         p = &buff[3];
  581.     (void)sprintf(path, "%s/%s", _PATH_RNEWSPROGS, p);
  582.     for (p = &path[sizeof _PATH_RNEWSPROGS]; *p; p++)
  583.         if (ISWHITE(*p)) {
  584.         *p = '\0';
  585.         break;
  586.         }
  587.     *fdp = StartChild(*fdp, path, cargv);
  588.     if (*fdp < 0)
  589.         return FALSE;
  590.     (*countp)++;
  591.     continue;
  592. #else
  593.     syslog(L_ERROR, "bad_format unknown command %s", buff);
  594.     return FALSE;
  595. #endif    /* defined(DO_RNEWSPROGS) */
  596.     }
  597.     return TRUE;
  598. }
  599.  
  600.  
  601. /*
  602. **  Read all articles in the spool directory and unpack them.  Print all
  603. **  errors with xperror as well as syslog, since we're probably being run
  604. **  interactively.
  605. */
  606. STATIC void
  607. Unspool()
  608. {
  609.     register DIR    *dp;
  610.     register DIRENTRY    *ep;
  611.     register BOOL    ok;
  612.     struct stat        Sb;
  613.     char        buff[SMBUF];
  614.     char        hostname[10];
  615.     int            fd;
  616.     int            oerrno;
  617.     int            i;
  618.  
  619.     /* Go to the spool directory, get ready to scan it. */
  620.     if (chdir(SPOOLNEWS) < 0) {
  621.     xperror(SPOOLNEWS);
  622.     syslog(L_FATAL, "cant cd %s %m", SPOOLNEWS);
  623.     exit(1);
  624.     }
  625.     if ((dp = opendir(".")) == NULL) {
  626.     xperror("Can't open spool directory");
  627.     syslog(L_FATAL, "cant opendir . %m");
  628.     exit(1);
  629.     }
  630.  
  631.     /* Loop over all files, and parse them. */
  632.     while ((ep = readdir(dp)) != NULL) {
  633.     InputFile = ep->d_name;
  634.     if (InputFile[0] == '.')
  635.         continue;
  636.     if (stat(InputFile, &Sb) < 0 && errno != ENOENT) {
  637.         xperror(InputFile);
  638.         syslog(L_ERROR, "cant stat %s %m", InputFile);
  639.         continue;
  640.     }
  641.  
  642.     if (!S_ISREG(Sb.st_mode))
  643.         continue;
  644.  
  645.     if ((fd = open(InputFile, O_RDONLY)) < 0) {
  646.         if (errno != ENOENT) {
  647.         xperror(InputFile);
  648.         syslog(L_ERROR, "cant open %s %m", InputFile);
  649.         }
  650.         continue;
  651.     }
  652.     (void)strncpy(hostname, InputFile, 8);
  653.     hostname[8] = '\0';
  654.     UUCPHost = hostname;
  655.     ok = UnpackOne(&fd, &i);
  656.     (void)close(fd);
  657.     WaitForChildren(i);
  658.  
  659.     if (!ok) {
  660.         oerrno = errno;
  661.         TempName(_PATH_BADNEWS, buff);
  662.         xperror("Unspooling failed");
  663.         (void)fprintf(stderr, "Unspooling failed saving to %s %s\n",
  664.         buff, strerror(errno));
  665.         errno = oerrno;
  666.         syslog(L_ERROR, "cant unspool saving to %s %m", buff);
  667.         if (rename(InputFile, buff) < 0) {
  668.         xperror(buff);
  669.         syslog(L_FATAL, "cant rename %s to %s %m", InputFile, buff);
  670.         exit(1);
  671.         }
  672.         continue;
  673.     }
  674.  
  675.     if (unlink(InputFile) < 0)
  676.         syslog(L_ERROR, "cant remove %s %m", InputFile);
  677.     }
  678.     (void)closedir(dp);
  679. }
  680.  
  681.  
  682.  
  683. /*
  684. **  Can't connect to the server, so spool our input.  There isn't much
  685. **  we can do if this routine fails, unfortunately.  Perhaps try to use
  686. **  an alternate filesystem?
  687. */
  688. STATIC void
  689. Spool(fd)
  690.     register int    fd;
  691. {
  692.     register int    spfd;
  693.     register int    i;
  694.     register int    j;
  695.     register char    *p;
  696.     char        temp[BUFSIZ];
  697.     char        buff[BUFSIZ];
  698.     int            count;
  699.     int            status;
  700.  
  701.     TempName(SPOOLTEMP, temp);
  702.     (void)umask(0);
  703.     if ((spfd = open(temp, O_WRONLY | O_CREAT, BATCHFILE_MODE)) < 0) {
  704.     syslog(L_FATAL, "cant open %s %m", temp);
  705.     exit(1);
  706.     }
  707.  
  708.     /* Read until we there is nothing left. */
  709.     for (status = 0, count = 0; (i = read(fd, buff, sizeof buff)) != 0; ) {
  710.     /* Break out on error. */
  711.     if (i < 0) {
  712.         syslog(L_FATAL, "cant read after %d %m", count);
  713.         status++;
  714.         break;
  715.     }
  716.     /* Write out what we read. */
  717.     for (count += i, p = buff; i; p += j, i -= j)
  718.         if ((j = write(spfd, (POINTER)p, (SIZE_T)i)) <= 0) {
  719.         syslog(L_FATAL, "cant write around %d %m", count);
  720.         status++;
  721.         break;
  722.         }
  723.     }
  724.  
  725.     /* Close the file. */
  726.     if (close(spfd) < 0) {
  727.     syslog(L_FATAL, "cant close spooled rnews %m");
  728.     status++;
  729.     }
  730.  
  731.     /* Move temp file into the spool area, and exit appropriately. */
  732.     TempName(SPOOLNEWS, buff);
  733.     if (rename(temp, buff) < 0) {
  734.     syslog(L_FATAL, "cant rename %s to %s %m", temp, buff);
  735.     status++;
  736.     }
  737.     exit(status);
  738.     /* NOTREACHED */
  739. }
  740.  
  741.  
  742. /*
  743. **  Try to read the password file and open a connection to a remote
  744. **  NNTP server.
  745. */
  746. STATIC BOOL
  747. OpenRemote(server, buff)
  748.     char    *server;
  749.     char    *buff;
  750. {
  751.     int        i;
  752.  
  753.     /* Open the remote connection. */
  754.     if (server)
  755.     i = NNTPconnect(server, &FromServer, &ToServer, buff);
  756.     else
  757.     i = NNTPremoteopen(&FromServer, &ToServer, buff);
  758.     if (i < 0)
  759.     return FALSE;
  760.  
  761.     *buff = '\0';
  762.     if (NNTPsendpassword((char *)NULL, FromServer, ToServer) < 0) {
  763.     (void)fclose(FromServer);
  764.     (void)fclose(ToServer);
  765.     return FALSE;
  766.     }
  767.     return TRUE;
  768. }
  769.  
  770.  
  771. /*
  772. **  Can't connect to server; print message and spool if necessary.
  773. */
  774. STATIC NORETURN
  775. CantConnect(buff, mode, fd)
  776.     char    *buff;
  777.     int        mode;
  778.     int        fd;
  779. {
  780.     if (buff[0])
  781.     syslog(L_NOTICE, "rejected connection %s", REMclean(buff));
  782.     else
  783.     syslog(L_FATAL, "cant open_remote %m");
  784.     if (mode != 'U')
  785.     Spool(fd);
  786.     exit(1);
  787. }
  788.  
  789.  
  790. /*
  791. **  Log an incorrect usage.
  792. */
  793. STATIC NORETURN
  794. Usage()
  795. {
  796.     syslog(L_FATAL, "usage error");
  797.     exit(1);
  798. }
  799.  
  800.  
  801. int
  802. main(ac, av)
  803.     int        ac;
  804.     char    *av[];
  805. {
  806.     int        fd;
  807.     int        i;
  808.     int        mode;
  809.     char    buff[SMBUF];
  810.     char    *Slave;
  811.  
  812.     /* First thing, set up logging and our identity. */
  813.     openlog("rnews", L_OPENLOG_FLAGS, LOG_INN_PROG);
  814.     if (setgid(getegid()) < 0) {
  815.     syslog(L_FATAL, "cant setgid to %d %m", getegid());
  816.     exit(1);
  817.     }
  818.     if (setuid(geteuid()) < 0) {
  819.     syslog(L_FATAL, "cant setuid to %d %m", geteuid());
  820.     exit(1);
  821.     }
  822.     UUCPHost = getenv(_ENV_UUCPHOST);
  823.     (void)umask(NEWSUMASK);
  824.  
  825.     /* Parse JCL. */
  826.     fd = STDIN;
  827.     mode = '\0';
  828.     Slave = NULL;
  829.     while ((i = getopt(ac, av, "h:S:Uv")) != EOF)
  830.     switch (i) {
  831.     default:
  832.         Usage();
  833.         /* NOTRTEACHED */
  834.     case 'h':
  835.         UUCPHost = *optarg ? optarg : NULL;
  836.         break;
  837.     case 'S':
  838.         Slave = optarg;
  839.         break;
  840.     case 'U':
  841.         mode = i;
  842.         break;
  843.     case 'v':
  844.         Verbose = TRUE;
  845.         break;
  846.     }
  847.     ac -= optind;
  848.     av += optind;
  849.  
  850.     /* Parse arguments.  At most one, the input file. */
  851.     switch (ac) {
  852.     default:
  853.     Usage();
  854.     /* NOTREACHED */
  855.     case 0:
  856.     break;
  857.     case 1:
  858.     if (mode == 'U')
  859.         Usage();
  860.     if (freopen(av[0], "r", stdin) == NULL) {
  861.         syslog(L_FATAL, "cant freopen %s %m", av[0]);
  862.         exit(1);
  863.     }
  864.     fd = fileno(stdin);
  865.     InputFile = av[0];
  866.     break;
  867.     }
  868.  
  869.     /* Open the link to the server. */
  870.     if (Slave) {
  871.     if (!OpenRemote(Slave, buff))
  872.         CantConnect(buff, mode, fd);
  873.     }
  874.     else {
  875. #if    defined(DO_RNEWSLOCALCONNECT)
  876.     if (NNTPlocalopen(&FromServer, &ToServer, buff) < 0) {
  877.         /* If server rejected us, no point in continuing. */
  878.         if (buff[0])
  879.         CantConnect(buff, mode, fd);
  880.         if (!OpenRemote((char *)NULL, buff))
  881.         CantConnect(buff, mode, fd);
  882.     }
  883. #else
  884.     if (!OpenRemote((char *)NULL, buff))
  885.         CantConnect(buff, mode, fd);
  886. #endif    /* defined(DO_RNEWSLOCALCONNECT) */
  887.     }
  888.     CloseOnExec((int)fileno(FromServer), TRUE);
  889.     CloseOnExec((int)fileno(ToServer), TRUE);
  890.  
  891.     /* Execute the command. */
  892.     if (mode == 'U')
  893.     Unspool();
  894.     else {
  895.     if (!UnpackOne(&fd, &i))
  896.         Spool(fd);
  897.     WaitForChildren(i);
  898.     }
  899.  
  900.     /* Tell the server we're quitting, get his okay message. */
  901.     (void)fprintf(ToServer, "quit\r\n");
  902.     (void)fflush(ToServer);
  903.     (void)fgets(buff, sizeof buff, FromServer);
  904.  
  905.     /* Return the appropriate status. */
  906.     exit(0);
  907.     /* NOTREACHED */
  908. }
  909.